#region Using Statements
using System.ComponentModel;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using System.Diagnostics;
using System.ComponentModel;
using System;
#endregion

namespace The_LarX
{
    public class Sprite2D
    {
        #region Variables

        #region Rendering

        public bool CalculateSizeRectangle = true;

        /// <summary>
        /// If it should Scale to Window Size. If the Screen
        /// Resolution is 640x480, the Sprite will be 640x480.
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("If it should Scale to Window Size. If the Screen Resolution is 640x480, the Sprite will be 640x480.")]
#endif
        public bool ResizeToWindow
        {
            get { return resizeToWindow; }
            set { resizeToWindow = value; }
        }
        protected bool resizeToWindow = false;

        /// <summary>
        /// The Camera2D to use
        /// </summary>
        public Camera2D Camera;

        /// <summary>
        /// 
        /// </summary>
        public SpriteSortMode SortMode = SpriteSortMode.BackToFront;

        public BlendState BlendState = BlendState.NonPremultiplied;

        /// <summary>
        /// EDITOR ONLY. Scale Proportinally to Window Size. This will resize the 
        /// Sprite to the screen resolution. 
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("Scale Proportinally to Window Size")]
#endif
        public bool ScaleProportionallyToWindow
        {
            get { return scaleProportionallyToWindow; }
            set
            {
                if (value)
                {
                    ScreenWidth = Engine.GraphicsDevice.Viewport.Width;
                    ScreenHeight = Engine.GraphicsDevice.Viewport.Height;
                }
                scaleProportionallyToWindow = value;
            }
        }
        private bool scaleProportionallyToWindow;
        private float ScreenWidth;
        private float ScreenHeight;
                
        /// <summary>
        /// The location of the Sprite Texture File. You can use
        /// XNB compiled Texture2D files, or *.larimg (just don't forget
        /// that if you are using larimg files, the TextureName must have 
        /// the extension *.larimg in it's end.
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The location of the Sprite Texture File")]
#endif
        public string TextureName
        {
            get { return textureName; }
            set
            {
                ChangeTex(value);
            }
        }

        protected Texture2D texture;
        /// <summary>
        /// The Texture2D this Sprite will draw. Don't set this 
        /// if you want to save the Sprite, instead set the TextureName
        /// value.
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The Texture2D")]
#endif
        public Texture2D Texture
        {
            get { return texture; }
            set { texture = value; }
        }
        protected string textureName;

        /// <summary>
        /// If the Sprite is Visible.
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("If the Sprite is Visible")]
#endif
        public bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }
        protected bool visible = true;

        protected void ChangeTex(string tex)
        {
            if (!string.IsNullOrEmpty(tex))
            {
                if (tex.Contains(".larimg"))
                {
                    texture = (Engine.Content.LoadLar(tex) as LarImage).Texture2D;
                    SourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
                }
                else
                {
                    texture = Engine.Content.Load<Texture2D>(tex);
                    SourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
                }
            }
            textureName = tex;
        }



        /// <summary>
        /// The SpriteEffects to be Applied
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The SpriteEffects to be Applied")]
#endif
        public SpriteEffects SpriteEffects
        {
            get { return spriteEffects; }
            set { spriteEffects = value; }
        }
        protected SpriteEffects spriteEffects;

        /// <summary>
        /// The text of this Sprite. The Text can be a Script. See Documentation for more details
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The text of this Sprite. The Text can be a Script. See Documentation for more details")]
#endif
        public string Text
        {
            get
            {
                if (TextIsScript)
                {
                    return ScriptText;
                }
                else
                {
                    return text;
                }
            }
            set
            {
                if (value != null)
                {
                    if (value.StartsWith("script:"))
                    {
                        TextIsScript = true;
                        ScriptText = value;
                        text = (string)Script.RunScript(value);
                        if (string.IsNullOrEmpty(text))
                        {
                            text = value;
                        }
                    }
                    else
                    {
                        text = value;
                    }
                }
            }
        }
        protected string text = String.Empty;
        protected string ScriptText;

        protected bool TextIsScript = false;

        /// <summary>
        /// Shadow Distance from the Position
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("Shadow Distance from the Position")]
#endif
        public float Shadow
        {
            get { return shadow; }
            set { shadow = value; }
        }
        protected float shadow = 2;

        /// <summary>
        /// The FontName - The Initial Value is Kootenay
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The FontName - The Initial Value is Kootenay")]        
#endif
        public string FontName
        {
            get { return fontName; }
            set { fontName = value; }
        }
        protected string fontName = "Kootenay";

        /// <summary>
        /// This Sprite's Color
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The Sprite's Color")]
#endif
        public Color Color
        {
            get { return color; }
            set { color = value; }
        }
        protected Color color = Color.White;

        protected int drawOrder = 0;
        /// <summary>
        /// The Order that it Should Be Drawed
        /// </summary>      
#if WINDOWS
        [Category("Rendering")]
        [Description("The Order that it Should Be Drawed")]
#endif
        public int DrawOrder
        {
            get { return drawOrder; }
            set { drawOrder = value; }
        }

        /// <summary>
        /// SourceRectangle
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The SourceRectangle")]
#endif
        public Rectangle SourceRectangle
        {
            get { return sourceRectangle; }
            set { sourceRectangle = value; }
        }
        protected Rectangle sourceRectangle;

        /// <summary>
        /// Gets the SizeRectangle
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("The SizeRectangle")]
#endif
        public Rectangle SizeRectangle
        {
            get { return sizeRectangle; }
            set { sizeRectangle = value; }
        }
        protected Rectangle sizeRectangle;

        /// <summary>
        /// If True, shows the SizeRectangle
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("If True, shows the SizeRectangle")]
#endif
        public bool DrawRectangle
        {
            get { return drawRectangle; }
            set { drawRectangle = value; }
        }
        protected bool drawRectangle = false;
        /// <summary>
        /// Rectangle Color, if Rendered
        /// </summary>
#if WINDOWS
        [Category("Rendering")]
        [Description("Rectangle Color, if Rendered")]
#endif
        public Color RectangleColor = Color.White;

        #endregion

        #region General

        /// <summary>
        /// The name of the Sprite
        /// </summary>
#if WINDOWS
        [Category("General")]
        [Description("The name of the Sprite")]
#endif
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        protected string name = string.Empty;

        /// <summary>
        /// Father Component. Can be null;
        /// </summary>
#if WINDOWS
        [Category("General")]
        [Description("The Component Father of this Sprite. Can be null")]
#endif
        public Component Parent
        {
            get { return parent; }
            set { parent = value; }
        }
        protected Component parent;

        #endregion

        #region Manipulation

        /// <summary>
        /// The Size of the Sprite
        /// </summary>
#if WINDOWS
        [Category("Manipulation")]
        [Description("The Size of the Sprite")]
#endif
        public Vector2 Size
        {
            get { return size; }
            set { size = value; }
        }
        protected Vector2 size = Vector2.One;

        /// <summary>
        /// The rotation of this Sprite, in Degrees
        /// </summary>
#if WINDOWS
        [Category("Manipulation")]
        [Description("The rotation of this Sprite, in Degrees")]
#endif
        public float Rotation
        {
            get { return MathHelper.ToDegrees(rotation); }
            set { rotation = MathHelper.ToRadians(value); }
        }
        protected float rotation = 0;


        /// <summary>
        /// Sprite's Position
        /// </summary>
#if WINDOWS
        [Category("Manipulation")]
        [Description("The Position in the Screen of this Sprite")]
#endif
        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }
        protected Vector2 position;

        /// <summary>
        /// Sprite's Origin
        /// </summary>
#if WINDOWS
        [Category("Manipulation")]
        [Description("Sprite's Origin")]
#endif
        public Vector2 Origin
        {
            get { return origin; }
            set { origin = value; }
        }
        protected Vector2 origin;

        #endregion                   

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor 1 - Loads a Texture for this Sprite
        /// </summary>
        /// <param name="TextureName">Texture Location</param>
        /// <param name="Position">Position</param>
        public Sprite2D(string TextureName, Vector2 Position)
        {            
            this.TextureName = TextureName;
            this.Position = Position;
            EngineSystem.Sprites.Add(this);           
            this.Name = "Sprite Random Name:"  + RandomUtil.Next(0, 999999999).ToString();
        }
        /// <summary>
        /// Constructor 2 - Loads a Text for this SPrite
        /// </summary>
        /// <param name="Position">Position</param>
        /// <param name="Text">The Text</param>
        public Sprite2D(Vector2 Position, string Text)
        {
            this.Text = Text;
            this.Position = Position;
            EngineSystem.Sprites.Add(this);
            this.Name = "Sprite Random Name:"  + RandomUtil.Next(0, 999999999).ToString();
        }
        /// <summary>
        /// Construtor 3 - Used for SERIALIZING - This doesn't initializes the Sprite. If you use this,
        /// you will have to manually set the Text/Position/Texture values for Rendering work.
        /// </summary>
        public Sprite2D() 
        {
            EngineSystem.Sprites.Add(this);
            this.Name = "Sprite Random Name:"  + RandomUtil.Next(0, 999999999).ToString();
        }
        
        #endregion

        #region Update and Draw

        public virtual void Disable()
        {
            EngineSystem.Sprites.Remove(this);
        }

        public virtual void Update()
        {
            Origin = Vector2.Zero;

            if (Text == String.Empty)
            {
                if (Texture != null)
                {
                    if (CalculateSizeRectangle)
                    {
                        float sizeX = Texture.Width * Size.X;
                        float sizeY = Texture.Height * Size.Y;
                        float posX = Position.X;
                        float posY = Position.Y;
                        SizeRectangle = new Rectangle((int)posX, (int)posY, (int)sizeX, (int)sizeY);
                    }
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(Text))
                {
                    if (CalculateSizeRectangle)
                    {
                        SpriteFont font = Engine.FontManager.GetFont(FontName);
                        Vector2 measure = font.MeasureString(text);
                        float sizeX = measure.X * Size.X;
                        float sizeY = measure.Y * Size.Y;
                        SizeRectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)sizeX, (int)sizeY);
                    }
                }
            }

            if (resizeToWindow)
            {
                SizeRectangle = new Rectangle((int)Position.X, (int)Position.Y, 
                    Engine.GraphicsDevice.Viewport.Width, Engine.GraphicsDevice.Viewport.Height);
            }
        }

        public int ShadowAlpha = 255;

        public virtual void Draw()
        {
            DrawSprite();
        }
        private void DrawSprite()
        {
            if (Visible)
            {
                if (DrawRectangle)
                {
                    Engine.RenderManager.DrawRectangle(RectangleColor, SizeRectangle, Camera, (int)Shadow, ShadowAlpha);
                }
                if (Texture != null)
                {
                    if (!resizeToWindow)
                    {
                        Engine.RenderManager.Draw(Texture, Position, SourceRectangle, 
                            Color, Rotation, Origin, Size, SpriteEffects, 0, Camera, true);
                    }
                    else
                    {
                        Engine.RenderManager.Draw(Texture, SizeRectangle, SourceRectangle,
                            Color, Rotation, Origin, SpriteEffects, 0, Camera, true, 
                            Engine.RenderManager.DefaultSortMode,
                        Engine.RenderManager.DefaultBlendState);
                    }
                }
                if (!string.IsNullOrEmpty(text))
                {
                    Engine.RenderManager.DrawString(text, Shadow, ShadowAlpha, 
                        Position, FontName, Color, Rotation, Origin, Size, 0, Camera, true, Engine.RenderManager.DefaultSortMode,
                        Engine.RenderManager.DefaultBlendState);
                }
            }
        }

        #endregion

        #region Serialization
        public virtual void SerializeData(SerializeData Data)
        {
            /// Rendering
            Data.AddData("Sprite.ResizeToWindow", this.ResizeToWindow);
            if (Camera != null)
                Data.AddData("Sprite.CameraName", this.Camera.Name);
            Data.AddData("Sprite.ScaleProportionallyToWindow", this.ScaleProportionallyToWindow);
            if (this.Texture != null)
            {
                Data.AddData("Sprite.Texture", this.textureName);
            }
            Data.AddData("Sprite.Visible", this.Visible);
            Data.AddData("Sprite.Effects", (int)this.SpriteEffects);
            if (this.Text != null)
            {
                Data.AddData("Sprite.Text", this.text);
            }
            Data.AddData("Sprite.Shadowed", this.Shadow);
            Data.AddData("Sprite.Font", this.FontName);
            Data.AddData("Sprite.Color", this.Color);
            Data.AddData("Sprite.DrawOrder", this.DrawOrder);
            Data.AddData("Sprite.SourceRectangle", this.SourceRectangle);
            Data.AddData("Sprite.DrawRectangle", this.DrawRectangle);
            Data.AddData("Sprite.RectangleColor", this.RectangleColor);
            
            /// General
            Data.AddData("Sprite.Name", this.Name);
            Data.AddData("Sprite.TextIsScript", this.TextIsScript);
            Data.AddData("Sprite.ScriptText", this.ScriptText);
            
            /// Manipulation
            Data.AddData("Sprite.Position", this.Position);
            Data.AddData("Sprite.Rotation", this.Rotation);
            Data.AddData("Sprite.Size", this.Size);
            Data.AddData("Sprite.Origin", this.Origin);

            Data.AddData("Sprite.ScreenHeight", this.ScreenHeight);
            Data.AddData("Sprite.ScreenWidth", this.ScreenWidth);
        }
        public virtual void DeserializeData(SerializeData Data)
        {
            /// Rendering
            this.ResizeToWindow = Data.GetData<bool>("Sprite.ResizeToWindow");
            if (Data.ContainsData("Sprite.CameraName"))
            {
                string cameraName = Data.GetData<string>("Spite.CameraName");
                this.Camera = (Camera2D)EngineSystem.Component_ByName(cameraName);
            }
            this.scaleProportionallyToWindow = Data.GetData<bool>("Sprite.ScaleProportionallyToWindow");
            if (Data.ContainsData("Sprite.Texture"))
            {
                this.TextureName = Data.GetData<string>("Sprite.Texture");
            }
            this.Visible = Data.GetData<bool>("Sprite.Visible");
            this.SpriteEffects = (SpriteEffects)Data.GetData<int>("Sprite.Effects");
            if (Data.ContainsData("Sprite.Text"))
            {
                this.Text = Data.GetData<string>("Sprite.Text");
            }
            this.Shadow = Data.GetData<float>("Sprite.Shadowed");
            this.FontName = Data.GetData<string>("Sprite.Font");
            this.Color = Data.GetData<Color>("Sprite.Color");
            this.DrawOrder = Data.GetData<int>("Sprite.DrawOrder");
            this.SourceRectangle = Data.GetData<Rectangle>("Sprite.SourceRectangle"); //// WILL NOT WORK
            this.DrawRectangle = Data.GetData<bool>("Sprite.DrawRectangle");
            this.RectangleColor = Data.GetData<Color>("Sprite.RectangleColor");

            // General 
            this.Name = Data.GetData<string>("Sprite.Name");
            this.TextIsScript = Data.GetData<bool>("Sprite.TextIsScript");
            this.ScriptText = Data.GetData<string>("Sprite.ScriptText");

            // Manipulation
            this.Rotation = Data.GetData<float>("Sprite.Rotation");
            this.Size = Data.GetData<Vector2>("Sprite.Size");
            this.Position = Data.GetData<Vector2>("Sprite.Position");
            this.Origin = Data.GetData<Vector2>("Sprite.Origin");

            this.ScreenHeight = Data.GetData<float>("Sprite.ScreenHeight");
            this.ScreenWidth = Data.GetData<float>("Sprite.ScreenWidth");
            if (scaleProportionallyToWindow)
            {
                ResizeSprite(new Vector2(ScreenWidth, ScreenHeight),
                    new Vector2(Engine.GraphicsDevice.Viewport.Width, Engine.GraphicsDevice.Viewport.Height));
                
                ScaleProportionallyToWindow = true;
            }
            
            if (TextIsScript)
            {
                Text = ScriptText;
            }
        }
        private float ActualHeight;
        private float ActualWidth;
        public void ResizeSprite(Vector2 LastScreenResolution, Vector2 NewScreenResolution)
        {
            if (NewScreenResolution.X != ActualWidth)
            {
                float porcent = NewScreenResolution.X / LastScreenResolution.X;
                size.X *= porcent;
                shadow *= porcent;
                position.X *= porcent;
                ActualWidth = NewScreenResolution.X;
            }
            if (NewScreenResolution.Y != ActualHeight)
            {
                float porcent2 = NewScreenResolution.Y / LastScreenResolution.Y;
                size.Y *= porcent2;
                position.Y *= porcent2;
                ActualHeight = NewScreenResolution.Y;
            }
        }

        #endregion

        #region Public Methods
        public override string ToString()
        {
            if (string.IsNullOrEmpty(Name))
            {
                Name = "Sprite, Position:" + this.Position.ToString() + " " + textureName;
                return Name;
            }
            return Name;
        }
        #endregion
    }
}
